package com.cainli.log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.Thread.UncaughtExceptionHandler;

import android.content.Context;
import android.os.Debug;
import android.os.Environment;
import android.os.StatFs;
import android.text.format.DateFormat;
import android.util.Log;

/**
 * 
 * @author cainli 2013-1-23下午6:58:41
 * 
 */
public class TLog {
	
	private static PrintStream ps;

	/**
	 * 是否开启日志
	 */
	public static boolean LOGGING = false;
	/**
	 * 日志的文件名
	 */
	public static String LOG_FILE = "";

	private static final String FORMAT_STR = "[yyyy-MM-dd hh:mm:ss a] ";

	public static void init(Context context) {
		String pacakgeName = context.getPackageName();
		LOGGING = "true".equalsIgnoreCase(System
				.getProperty(pacakgeName+".debug"));
		LOG_FILE = System.getProperty(pacakgeName+".logfile", "");
		if (!LOGGING) {
			return;
		}

		File logFile = null;
		// 有sd卡
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
			/**
			 * QLog的调用要放在application的进程判断之后
			 */
			logFile = new File(Environment.getExternalStorageDirectory(),
					"/android/data/" + context.getPackageName()
							+ "/_log/crash_" + LOG_FILE + ".txt");

			try {
				if (logFile.isDirectory()) {
					logFile.delete();
				}
				// 文件存在
				if (logFile.exists()) {
					FileInputStream fis = new FileInputStream(logFile);
					int fileLength = fis.available();
					// 大于1m,删除
					if (fileLength > 1 * 1024 * 1024) {
						logFile.delete();
						logFile.getParentFile().mkdirs();
						logFile.createNewFile();
					}
					// 小于1m，继续使用
				} else {
					int blockSize = new StatFs(Environment
							.getExternalStorageDirectory().getPath())
							.getBlockSize();
					int blockcount = new StatFs(Environment
							.getExternalStorageDirectory().getPath())
							.getAvailableBlocks();
					long remineSize = ((long) blockSize * blockcount);
					if (remineSize > 1 * 1024 * 1024) {
						logFile.getParentFile().mkdirs();
						logFile.createNewFile();
					}
				}
				if (logFile.exists()) {
					ps = new PrintStream(new FileOutputStream(logFile, true),
							true);
				} else {
					ps = System.out;
					// LOGGING = false;
				}
			} catch (IOException e) {
				Log.e("ParandroidInstrumentation", "", e);
				ps = System.out;
				// LOGGING = false;
			}
		} else {
			ps = System.out;
			// LOGGING = false;
		}

	}

	private TLog() {

	}

	public static class ExceptionHandle implements UncaughtExceptionHandler {
		UncaughtExceptionHandler old;

		/**
		 * 初始化
		 */
		public static void initDefaultUncaughtExceptionHandler() {
			Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandle());
		}

		private ExceptionHandle() {
			old = Thread.getDefaultUncaughtExceptionHandler();
		}

		@Override
		public void uncaughtException(Thread thread, Throwable ex) {
			// 未捕获的异常
			if (LOGGING) {
				if (ex instanceof OutOfMemoryError) {
					// 打印 dump
					try {
						Debug.dumpHprofData("/sdcard/dump.hprof");
					} catch (IOException e) {
						e.printStackTrace(ps);
					}
				}
				ps.print(DateFormat.format(FORMAT_STR,
						System.currentTimeMillis()));
				ps.println("Un Caught Exception.");
				ex.printStackTrace(ps);
				ps.println();
				ps.flush();
			}
			old.uncaughtException(thread, ex);
		}
	}

	/**
	 * Send a {@link #VERBOSE} log message.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 */
	public static int v(String tag, String msg) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			return Log.v(tag, msg);
		}
		return 0;
	}

	/**
	 * Send a {@link #VERBOSE} log message and log the exception.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 * @param tr
	 *            An exception to log
	 */
	public static int v(String tag, String msg, Throwable tr) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			return Log.v(tag, msg, tr);
		}
		return 0;
	}

	/**
	 * Send a {@link #DEBUG} log message.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 */
	public static int d(String tag, String msg) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			return Log.d(tag, msg);
		}
		return 0;
	}

	/**
	 * Send a {@link #DEBUG} log message and log the exception.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 * @param tr
	 *            An exception to log
	 */
	public static int d(String tag, String msg, Throwable tr) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			return Log.d(tag, msg, tr);
		}
		return 0;
	}

	/**
	 * Send an {@link #INFO} log message.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 */
	public static int i(String tag, String msg) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			return Log.i(tag, msg);
		}
		return 0;
	}

	/**
	 * Send a {@link #INFO} log message and log the exception.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 * @param tr
	 *            An exception to log
	 */
	public static int i(String tag, String msg, Throwable tr) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			return Log.i(tag, msg, tr);
		}
		return 0;
	}

	/**
	 * Send a {@link #WARN} log message.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 */
	public static int w(String tag, String msg) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			ps.print(DateFormat.format(FORMAT_STR, System.currentTimeMillis()));
			ps.println("WARN: " + tag + " \t " + msg);
			return Log.w(tag, msg);
		}
		return 0;
	}

	/**
	 * Send a {@link #WARN} log message and log the exception.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 * @param tr
	 *            An exception to log
	 */
	public static int w(String tag, String msg, Throwable tr) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			ps.print(DateFormat.format(FORMAT_STR, System.currentTimeMillis()));
			ps.println("WARN: " + tag + " \t " + msg);
			tr.printStackTrace(ps);
			ps.println();
			return Log.w(tag, msg, tr);
		}
		return 0;
	}

	/*
	 * Send a {@link #WARN} log message and log the exception.
	 * 
	 * @param tag Used to identify the source of a log message. It usually
	 * identifies the class or activity where the log call occurs.
	 * 
	 * @param tr An exception to log
	 */
	public static int w(String tag, Throwable tr) {
		if (LOGGING) {
			ps.print(DateFormat.format(FORMAT_STR, System.currentTimeMillis()));
			ps.println("WARN: " + tag);
			tr.printStackTrace(ps);
			ps.println();
			return Log.w(tag, tr);
		}
		return 0;
	}

	/**
	 * Send an {@link #ERROR} log message.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 */
	public static int e(String tag, String msg) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			ps.print(DateFormat.format(FORMAT_STR, System.currentTimeMillis()));
			ps.println("ERROR: " + tag + " \t " + msg);
			return Log.e(tag, msg);
		}
		return 0;
	}

	/**
	 * Send a {@link #ERROR} log message and log the exception.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 * @param tr
	 *            An exception to log
	 */
	public static int e(String tag, Throwable tr) {
		return e(tag, tr.getMessage(), tr);
	}

	/**
	 * Send a {@link #ERROR} log message and log the exception.
	 * 
	 * @param tag
	 *            Used to identify the source of a log message. It usually
	 *            identifies the class or activity where the log call occurs.
	 * @param msg
	 *            The message you would like logged.
	 * @param tr
	 *            An exception to log
	 */
	public static int e(String tag, String msg, Throwable tr) {
		if (LOGGING) {
			if (msg == null) {
				msg = "";
			}
			ps.print(DateFormat.format(FORMAT_STR, System.currentTimeMillis()));
			ps.println("ERROR: " + tag + " \t " + msg);
			tr.printStackTrace(ps);
			ps.println();
			return Log.e(tag, msg, tr);
		}
		return 0;
	}

	/**
	 * Handy function to get a loggable stack trace from a Throwable
	 * 
	 * @param tr
	 *            An exception to log
	 */
	public static String getStackTraceString(Throwable tr) {
		if (tr == null) {
			return "";
		}
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		tr.printStackTrace(pw);
		return sw.toString();
	}
}